package com.assisjrs.jpaandroid.persistence;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

import android.content.ContentValues;
import android.database.Cursor;

import com.assisjrs.jpaandroid.persistence.Entity;
import com.assisjrs.jpaandroid.persistence.Transient;

public class ModelDefinition {
	public static final String ID_NAME = "id";

	private final Class<? extends Entity> entityClass;
	private final List<FieldDefinition> fields = new ArrayList<FieldDefinition>();

	private String tableName;
	private String createScript;
	private String dropScript;

	public ModelDefinition(final Class<? extends Entity> entityClass) {
		this.entityClass = entityClass;
		scan();
	}

	public String getCreateScript() {
		return createScript;
	}

	public String getDropScript() {
		return dropScript;
	}

	public String getTableName() {
		return tableName;
	}

	public Class<? extends Entity> getEntityClass() {
		return entityClass;
	}

	public List<FieldDefinition> getFields() {
		return fields;
	}

	public ContentValues getContentValues(final Entity entity) {
		final ContentValues cv = new ContentValues();
		cv.put(ID_NAME, entity.getId());

		for (final FieldDefinition field : fields) {
			final Object value = field.get(entity);

			if (value == null) {
				cv.putNull(field.getColumnName());
			} else
				cv.put(field.getColumnName(), value.toString());
		}

		return cv;
	}

	public void fill(final Entity entity, final Cursor cursor) {
		final int idColumnIndex = cursor.getColumnIndex(ID_NAME);
		final long id = cursor.getLong(idColumnIndex);
		entity.setId(id);

		for (final FieldDefinition field : fields) {
			field.set(entity, cursor);
		}
	}

	public Entity newInstance(final Cursor cursor) {
		final Entity entity = newInstance();

		fill(entity, cursor);

		return entity;
	}

	public Entity newInstance() {
		try {
			return getEntityClass().newInstance();
		} catch (final IllegalAccessException e) {
			throw new RuntimeException(e);
		} catch (final InstantiationException e) {
			throw new RuntimeException(e);
		}
	}

	private void scan() {
		scanFields(entityClass);

		tableName = entityClass.getSimpleName();
		dropScript = "DROP TABLE IF EXISTS " + tableName;
		createScript = createScript();
	}

	private String createScript() {
		final StringBuilder sb = new StringBuilder();

		sb.append("CREATE TABLE ");
		sb.append(tableName);
		sb.append(" (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT");

		for (final FieldDefinition field : fields) {
			sb.append(", ");
			sb.append(field.getCreateScript());
		}

		sb.append(");");

		return sb.toString();
	}

	private void scanFields(final Class<?> entityClass) {
		if (entityClass == Object.class)
			return;

		for (final Field field : entityClass.getDeclaredFields()) {
			if (ID_NAME.equals(field.getName()))
				continue;

			if (Modifier.isTransient(field.getModifiers()) || Modifier.isStatic(field.getModifiers())
			        || Modifier.isFinal(field.getModifiers()) || field.isAnnotationPresent(Transient.class))
				continue;

			fields.add(new FieldDefinition(field));
		}

		scanFields(entityClass.getSuperclass());
	}
}
